Descripción del programa

¿Que es R?

  • Lenguaje de Análisis de Datos
  • Software Libre
  • Sintaxis Básica: R base
  • Sintaxis incremental1: El lenguaje se va ampliando por aportes de Universidades, investigadores y empresas privadas, organizados en librerías (o paquetes)
  • Comunidad web muy grande para realizar preguntas y despejar dudas.

El entorno más cómodo para utilizar el lenguaje R es el programa R studio

  • Rstudio es una empresa que produce productos asociados al lenguaje R, como el programa sobre el que corremos los comandos, y extensiones del lenguaje (librerías).

  • El programa es gratuito y se puede bajar de la página oficial

Pantalla Rstudio

Pantalla Rstudio

Diferencias con STATA y SPSS

  • Gratuito
  • Únicamente funciona por líneas de código (No hay botones para ejecutar comandos)
  • Posibilita trabajar con múltiples bases de microdatos al mismo tiempo, sin mayor dificultad (No requiere abrir cada base, trabajarla por separado y luego cerrarla)
  • Más potente
    • Totalmente automatizable
    • Aportes de usuario
    • Extensible a otros lenguajes y usos (esta presentación)
  • Más veloz:

Lógica sintáctica.

Definición de objetos

El operador <- sirve para definir un elemento. A la izquierda del <- debe ubicarse el nombre que tomará el elemento a crear. Del lado derecho debe ir la definición del mismo

A <- 1

Al definir un elemento, el mismo queda guardado en el ambiente del programa, y podrá ser utilizado posteriormente para observar su contenido o para realizar una operación con el mismo

A 
[1] 1

Al correr una linea con el nombre del objeto, la consola del programa nos muestra su contenido. Entre Corchetes Observamos el número de orden del elemento en cuestión

El operador = es equivalente a <-, pero en la práctica no se utiliza para la definición de objetos.

B = 2
B
[1] 2

<- es un operador Unidireccional, es decir que:
A <- B implica que A va tomar como valor el contenido del objeto B, y no al revés.

A <- B
A   #Ahora A toma el valor de B, y B continua conservando el mismo valor
[1] 2
B
[1] 2

R base

Con R base nos referimos a los comandos básicos que vienen incorporados en el R, sin necesidad de cargar librerías.

Operadores lógicos:

  • \(>\)
  • \(>=\)
  • \(<\)
  • \(<=\)
  • \(==\)
  • \(!=\)
A = 1
B = 2
A >  B
[1] FALSE
A >= B
[1] FALSE
A <  B
[1] TRUE
A <= B
[1] TRUE
A == B
[1] FALSE
A != B
[1] TRUE
C <- A != B
C
[1] TRUE

Como muestra el último ejemplo, el resultado de una operación lógica puede almacenarse como el valor de un objeto.

Operadores aritméticos:

#suma
A <- 5+6
A
[1] 11
#Resta
B <- 6-8
B
[1] -2
#cociente
C <- 6/2.5
C
[1] 2.4
#multiplicacion
D <- 6*2.5
D
[1] 15

Caracteres especiales

  • R es sensible a mayúsculas y minúsculas, tanto para los nombres de las variables, como para las funciones y parámetros.
  • Los espacios en blanco y los carriage return (enter) no son considerados por el lenguaje. Los podemos aprovechar para emprolijar el código y que la lectura sea más simple2.
  • El numeral # se utiliza para hacer comentarios. Todo lo que se escribe después del # no es interpretado por R. Se debe utilizar un # por cada línea de código que se desea anular

  • Los corchetes [] se utilizan para acceder a un objeto:
    • en un vector[n° orden]
    • en una matriz[fila, columna]
    • en una lista[n° elemento]
  • el signo $ también es un método de acceso, que permite llamar al elemento por su nombre, en dataframes y listas.

  • Los paréntesis() se utilizan en las funciones para definir los parámetros.

  • Las comas , se utilizan para separar los elementos.

Ejemplo: si queremos definir al elemento de la fila 2 y columna 3 de una tabla como el resultado de una funcion con parámetros A=a, B=b y C=c, diremos:

tabla[2,3] <- funcion(A=a, B=b, C=c)

Objetos:

  • Valor
  • Vector
  • Matriz
  • Data Frame
  • Lista

Valores

Los valores tienen distintos tipos:

Numeric

A <-  1
class(A)
[1] "numeric"

Character

A <-  paste('Soy', 'una', 'concatenación', 'de', 'caracteres', sep = " ")
A
[1] "Soy una concatenación de caracteres"
class(A)
[1] "character"

Factor

A <- factor("Soy un factor, con niveles fijos")
class(A)
[1] "factor"

La diferencia entre un character y un factor es que el último tiene solo algunos valores permitidos (levels), y se le puede dar un orden

Date

A <- as.Date("2017-01-01")
class(A)
[1] "Date"

Vectores

Para crear un vector utilizamos el comando c(), de combinar. Puede tener variables de cualquier tipo

C <- c(1, 3, 4)
C
[1] 1 3 4

sumarle 2 a cada elemento del vector anterior

C <- C + 2
C
[1] 3 5 6

sumarle 1 al primer elemento, 2 al segundo, y 3 al tercer elemento del vector anterior

D <- C + 1:3 #esto es equivalente a hacer 3+1, 5+2, 6+9 
D
[1] 4 7 9

1:3 significa que queremos todos los números enteros desde 1 hasta 3.

crear un vector que contenga las palabras: Ingreso, Variación, Indice

E <- c("Carlos","Federico","Pedro")
E
[1] "Carlos"   "Federico" "Pedro"   

para acceder a algún elemento del vector, podemos buscarlo por su número de orden, entre [ ]

elemento2 <- E[2]
elemento2
[1] "Federico"

para borrar un objeto, utilizamos el comando rm()

rm(elemento2)
elemento2
Error: object 'elemento2' not found

También podemos cambiar el texto del segundo elemento de E, por el texto “var”

E[2] <- "Pablo"
E
[1] "Carlos" "Pablo"  "Pedro" 

Data Frames

Un Data Frame es una tabla de datos, donde cada columna representa una variable, y cada fila una observación. Son el equivalente a las tablas de STATA y SPSS

Este objeto suele ser central en el proceso de trabajo, y suele ser la forma en que se cargan datos externos, así como la mayoría de los elementos intermedios, hasta aquello que exportemos

También Se puede crear como la combinación de N vectores de igual tamaño. Por ejemplo, tomamos algunos valores del Indice de salarios

INDICE  <- c(100,   100,   100,
             101.8, 101.2, 100.73,
             102.9, 102.4, 103.2)
FECHA  <-  c("Oct-16", "Oct-16", "Oct-16",
             "Nov-16", "Nov-16", "Nov-16",
             "Dic-16", "Dic-16", "Dic-16")
GRUPO  <-  c("Privado_Registrado","Público","Privado_No_Registrado",
             "Privado_Registrado","Público","Privado_No_Registrado",
             "Privado_Registrado","Público","Privado_No_Registrado")
             
Datos <- data.frame(INDICE, FECHA, GRUPO)
Datos

Tal como en un vector se ubica a los elementos mediante [ ], en un dataframe se obtienen sus elementos de la forma [fila, columna].

Otra opción es seleccionar la columna, mediante el operador $, y luego seleccionar dentro de esa columna, por el número de orden.

Datos$FECHA
[1] Oct-16 Oct-16 Oct-16 Nov-16 Nov-16 Nov-16 Dic-16 Dic-16 Dic-16
Levels: Dic-16 Nov-16 Oct-16
Datos[3,2]
[1] Oct-16
Levels: Dic-16 Nov-16 Oct-16
Datos$FECHA[3]
[1] Oct-16
Levels: Dic-16 Nov-16 Oct-16

¿que pasa si hacemos Datos$FECHA[3,2] ?

Datos$FECHA[3,2]
Error in `[.default`(Datos$FECHA, 3, 2) : incorrect number of dimensions

Nótese que el último comando tiene un número incorrecto de dimensiones, porque estamos refiriendonos 2 veces a la columna FECHA.

Listas

Contienen una concatenación de objetos de cualquier tipo. Así como un vector contiene valores, un dataframe contiene vectores, una lista puede contener dataframes, pero también vectores, o valores, y todo ello a la vez

superlista <- list(A,B,C,D,E,FECHA, DF = Datos, INDICE, GRUPO)
superlista
[[1]]
[1] "2017-01-01"

[[2]]
[1] -2

[[3]]
[1] 3 5 6

[[4]]
[1] 4 7 9

[[5]]
[1] "Carlos" "Pablo"  "Pedro" 

[[6]]
[1] "Oct-16" "Oct-16" "Oct-16" "Nov-16" "Nov-16" "Nov-16" "Dic-16" "Dic-16"
[9] "Dic-16"

$DF

[[8]]
[1] 100.00 100.00 100.00 101.80 101.20 100.73 102.90 102.40 103.20

[[9]]
[1] "Privado_Registrado"    "Público"               "Privado_No_Registrado"
[4] "Privado_Registrado"    "Público"               "Privado_No_Registrado"
[7] "Privado_Registrado"    "Público"               "Privado_No_Registrado"

Para acceder un elemento de una lista, podemos utilizar el operador $, que se puede usar a su vez de forma iterativa

superlista$DF$FECHA[2]
[1] Oct-16
Levels: Dic-16 Nov-16 Oct-16

Loops, condicionales y funciones

Un loop es una estructura de código que nos permite recorrer iterativamente un conjunto de comandos, variando algún elemento. Por ejemplo:

for(i in 1:10){
  Cuadrados<- i^2
  print(Cuadrados)
}
[1] 1
[1] 4
[1] 9
[1] 16
[1] 25
[1] 36
[1] 49
[1] 64
[1] 81
[1] 100

esto se lee como : Para i, que toma los valores de 1 a 10: imprimí i.

También se puede tomar una lista de valores cualesquiera. Por ejemplo reutilizar datos de un dataframe:

Datos
unique(Datos$GRUPO)
[1] Privado_Registrado    Público               Privado_No_Registrado
Levels: Privado_No_Registrado Privado_Registrado Público
for(variable in unique(Datos$GRUPO)){
  print(Datos[Datos$GRUPO == variable,])
}

Las estructuras condiconales nos permites ejecutar código de forma condicional a que se cumpla determinada condición.

A
[1] "2017-01-01"
resultado <- ifelse(test = class(A) == "character", yes =  paste0("Esto es un caracter"),
       no = paste0("Esto no era un caracter"))
resultado
[1] "Esto no era un caracter"

Otra función para condicionar la ejecución de una porción del código es if(condicion){codigo a ejecutar si se cumple la condición}

Las funciones nos permiten automatizar todas aquellas partes del código que se repiten mucho. Una vez diseñadas, funcionan igual que cualquier comando. La facilidad para crear las funciones es en buena medida la explicación de que haya tantas contribuciones de usuarios a la expansión del lenguaje.

funcion_prueba <- function(a,b) {
  print(paste(a, b, sep = " "))
  
}
funcion_prueba(a = "soy la primera parte de un string.", b = "Y yo la segunda parte")
[1] "soy la primera parte de un string. Y yo la segunda parte"

También podemos asignar un valor por default.

funcion_prueba <- function(a = "Soy una primera parte default.",b) {
  print(paste(a, b, sep = " "))
  
}
funcion_prueba(b = "  A mi no me defaulteas")
[1] "Soy una primera parte default.   A mi no me defaulteas"

Las funciones que creamos nosotros viven dentro del script donde se las define. Es decir, es necesario volver a correr la definición cada vez que la queremos utilizar.

Vale mencionar que lo que ocurre en una función, queda en la función excepto que explícitamente pidamos que devuelva el resultado, con el comando print() o return()

Lectura y escritura de archivos

R tiene formatos de archivos propios:

  • Rdata
  • RDS

RData

x <- 1:15
y <- list(a = 1, b = TRUE, c = "oops")
#Para guardar
save(x, y, file = "xy.RData")
#Para leer
load('xy.RData')

Los archivos de tipo RData permiten grabar una imagen de todos los objetos R que querramos.

RDS

x
 [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
saveRDS(x, "x.RDS")
Z <- readRDS("x.RDS")
Z
 [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15

Los archivos de tipo RDS no guardan el nombre del objeto, por lo que podemos nombrarlos cuando los cargamos (aconsejable)

Archivos de otros formatos

Hay muchas funciones para leer archivos de tipo .txt y .csv. La mayoría sólo cambia los parámetros que vienen por default.

Es importante tener en cuenta:

  • encabezado
  • delimitador (,, tab, ;)
  • separador decimal
dataframe <- read.delim(file, header = TRUE, sep = "\t", quote = "\"", dec = ".", fill = TRUE, comment.char = "", ...) 

Ejemplo. Levantar la base individual de EPH del 1er trimestre 2017

individual_t117 <- read.table('//NE18106/Trabajos/Curso R/Fuentes/usu_individual_t117.txt',sep=";", dec=",", header = TRUE, fill = TRUE)
individual_t117

aprovechemos esta primera tabla en serio para introducir algunos comandos útiles para tener una mirada rápida de la base.

#View(individual_t117)
names(individual_t117)
  [1] "CODUSU"     "ANO4"       "TRIMESTRE"  "NRO_HOGAR"  "COMPONENTE" "H15"       
  [7] "REGION"     "MAS_500"    "AGLOMERADO" "PONDERA"    "CH03"       "CH04"      
 [13] "CH05"       "CH06"       "CH07"       "CH08"       "CH09"       "CH10"      
 [19] "CH11"       "CH12"       "CH13"       "CH14"       "CH15"       "CH15_COD"  
 [25] "CH16"       "CH16_COD"   "NIVEL_ED"   "ESTADO"     "CAT_OCUP"   "CAT_INAC"  
 [31] "IMPUTA"     "PP02C1"     "PP02C2"     "PP02C3"     "PP02C4"     "PP02C5"    
 [37] "PP02C6"     "PP02C7"     "PP02C8"     "PP02E"      "PP02H"      "PP02I"     
 [43] "PP03C"      "PP03D"      "PP3E_TOT"   "PP3F_TOT"   "PP03G"      "PP03H"     
 [49] "PP03I"      "PP03J"      "INTENSI"    "PP04A"      "PP04B_COD"  "PP04B1"    
 [55] "PP04B2"     "PP04B3_MES" "PP04B3_ANO" "PP04B3_DIA" "PP04C"      "PP04C99"   
 [61] "PP04D_COD"  "PP04G"      "PP05B2_MES" "PP05B2_ANO" "PP05B2_DIA" "PP05C_1"   
 [67] "PP05C_2"    "PP05C_3"    "PP05E"      "PP05F"      "PP05H"      "PP06A"     
 [73] "PP06C"      "PP06D"      "PP06E"      "PP06H"      "PP07A"      "PP07C"     
 [79] "PP07D"      "PP07E"      "PP07F1"     "PP07F2"     "PP07F3"     "PP07F4"    
 [85] "PP07F5"     "PP07G1"     "PP07G2"     "PP07G3"     "PP07G4"     "PP07G_59"  
 [91] "PP07H"      "PP07I"      "PP07J"      "PP07K"      "PP08D1"     "PP08D4"    
 [97] "PP08F1"     "PP08F2"     "PP08J1"     "PP08J2"     "PP08J3"     "PP09A"     
[103] "PP09A_ESP"  "PP09B"      "PP09C"      "PP09C_ESP"  "PP10A"      "PP10C"     
[109] "PP10D"      "PP10E"      "PP11A"      "PP11B_COD"  "PP11B1"     "PP11B2_MES"
[115] "PP11B2_ANO" "PP11B2_DIA" "PP11C"      "PP11C99"    "PP11D_COD"  "PP11G_ANO" 
[121] "PP11G_MES"  "PP11G_DIA"  "PP11L"      "PP11L1"     "PP11M"      "PP11N"     
[127] "PP11O"      "PP11P"      "PP11Q"      "PP11R"      "PP11S"      "PP11T"     
[133] "P21"        "DECOCUR"    "IDECOCUR"   "RDECOCUR"   "GDECOCUR"   "PDECOCUR"  
[139] "ADECOCUR"   "PONDIIO"    "TOT_P12"    "P47T"       "DECINDR"    "IDECINDR"  
[145] "RDECINDR"   "GDECINDR"   "PDECINDR"   "ADECINDR"   "PONDII"     "V2_M"      
[151] "V3_M"       "V4_M"       "V5_M"       "V8_M"       "V9_M"       "V10_M"     
[157] "V11_M"      "V12_M"      "V18_M"      "V19_AM"     "V21_M"      "T_VI"      
[163] "ITF"        "DECIFR"     "IDECIFR"    "RDECIFR"    "GDECIFR"    "PDECIFR"   
[169] "ADECIFR"    "IPCF"       "DECCFR"     "IDECCFR"    "RDECCFR"    "GDECCFR"   
[175] "PDECCFR"    "ADECCFR"    "PONDIH"    
summary(individual_t117)[,c(8,10,31,133)]
 MAS_500      PONDERA           IMPUTA              P21        
 N:31790   Min.   :  25.0   Min.   :0.000000   Min.   :    -9  
 S:26885   1st Qu.: 137.0   1st Qu.:0.000000   1st Qu.:     0  
           Median : 238.0   Median :0.000000   Median :     0  
           Mean   : 467.3   Mean   :0.002727   Mean   :  3671  
           3rd Qu.: 541.0   3rd Qu.:0.000000   3rd Qu.:  4800  
           Max.   :7001.0   Max.   :1.000000   Max.   :300000  
                                                               
head(individual_t117)[,1:5]

Excel

Para leer archivos excel debemos utilizar los comandos que vienen con la librería xlsx

# install.packages("xlsx") # por única vez
library(xlsx) #activamos la librería
#creamos una tabla cualquiera de prueba
x <- 1:10
y <- 11:20
tabla_de_R <- data.frame(x,y)
# escribimos el archivo
write.xlsx( x = tabla_de_R, file = "archivo.xlsx",sheetName = "hoja 1",row.names = FALSE)
#leemos el archivo
tabla <- read.xlsx(file = "archivo.xlsx",sheetName = "hoja 1")
tabla

SPSS, STATA, SAS

Podemos utilizar la librería haven, y los comandos:

  • read_spss()
  • read_dta()
  • read_sas()

Encoding

Tanto a la hora de leer y escribir archivos, como al trabajar un mismo script desde distintas computadoras, debemos ser cuidadosos con el encoding seteado. El encoding es el sistema mediante el cual el sistema interpreta los caracteres del lenguaje natural. Hay muchos encodings diferentes, que interpretan distinto algunos caracteres, como tildes y signos de puntuación.
Por ende, si el encoding seteado no es el mismo que el de nuestro script/tabla pueden generarse errores. En medida de lo posible, al escribir nuestros scripts es recomendable evitar estos caracteres.

R tiene por default el encoding “ISO-8859-1”, sin embargo el más habitual en América Latina es “UTF-8”.

  • Lectura de archivos : Aglunas de las funciones del tipo read_table, read_xlsx permiten establecer como uno de sus parametros el encoding deseado
  • Encoding utilizado para abrir un script:File -> Reopen with Encoding
  • Encoding default con el que se guardan nuestros Scripts: Tools -> Global Options -> Code -> Saving

Directorios

Siempre que escribimos el nombre del archivo, R lo busca en el working directory. Para saber cual es el directorio de trabajo utilizamos la función getwd(). Para redefinir el directorio de trabajo, utilizamos la función setwd

No es aconsejable utilizar el directorio de trabajo, si nos olvidamos de definirlo, tiramos los archivos en cualquier lado

Lo más práctico es definir los directorios de trabajo como valores. y pegar el nombre del archivo con las carpetas.

carpeta_fuentes    <- paste("C:/Users/.../Documents/R/fuentes/")
carpeta_resultados <- paste("C:/Users/.../Documents/R/resultados/")

Es importante notar que se utiliza la barra / en lugar de \ (que sale de copiar y pegar el directorio)

el nombre completo del archivo puede ser

archivo_datos      <- paste0(carpeta_fuentes, "archivo_fuentes.txt")
archivo_resultados <- paste0(carpeta_resultados, "archivo_resultados.txt")

luego, para leer un excel, se escribe:

tabla <- read.xlsx(file = archivo_datos,sheetName = "hoja 1") #como es una variable, ya no lleva comillas

Directorios autorreferenciales

Si bien excede los alcances de este curso, dejamos un chunk de código que puede resultar sumamente útil para crear un directorio de trabajo para un proyecto nuevo.

#install.packages(rstudioapi)
script.dir <- paste0(dirname(rstudioapi::getActiveDocumentContext()$path),"/")
bases.dir  <-  paste0(dirname(script.dir),"/Fuentes/")
#dir.create(bases.dir)
resultados.dir <- paste0(dirname(script.dir),"/Resultados/")
#dir.create(resultados.dir)
#chequeo
dir.exists(bases.dir)
[1] TRUE
dir.exists(resultados.dir)
[1] TRUE

La primera línea encuentra la carpeta donde esta guardado el script (si no esta guardado no funciona).
La segunda línea crea el nombre del directorio Fuentes La tercera línea (anulada) crea el directorio Fuentes La cuarta línea crea el nombre del directorio Resultados La quinta línea (anulada) crea el directorio Resultados

Organización scripts

Por último, es aconsejable mantener en todos los script una misma estructura del tipo:

  1. Limpiar la memoria rm(list=ls())
  2. Cargar librerías
  3. Definir directorios
  4. Definir funciones
  5. Levantar archivos
    … procesamiento ….
  1. grabar resultados

También es útil organizar las partes del script en capítulos. Para eso

### escribimos el título del capitulo encerrado entre tres o más corchetes ###

Ayudas

Hay muchas ayudas, propias del programa, o de usuarios, que pueden ser de ayuda.

  • En el programa, para consultar los parámetros de una función, le escribe ?funcion()

  • Rstudio tiene unos machetes muy útiles

  • Rdocumentation

  • stack overflow conviene llegar desde google

La clave es aprender la terminología para googlear en ingles las dudas, y prestar atención a que las respuestas sean actuales (R es un lenguaje vivo)

Para la reunión 2:

instalar los siguientes paquetes:

install.packages(c("tidyverse","xlsx",'statar','reldist', 'ggthemes', 'ggrepel', 'scales', 'ggjoy', 'rstudioapi','questionr', 'alluvial'))

Ejercicios para practicar

  • Crear un OBJETO llamado OBJETO definido como el resultado de la suma: 5 + 6
  • Crear un VECTOR VEC0 que contenga los números 1, 3, 4.
  • Crear 3 vectores ( VEC1, VEC2, VEC3) que sean transformaciones del anterior
  • Crear 3 vectores con la misma cantidad de elementos que VEC0, pero con variables string (texto) ( VEC4, VEC5, VEC6).
  • Crear un dataframe DFRAME como combinación de todos los vectores creados previamente
  • Crear una lista con cada uno de los elementos creados previamente

  • Para todos los valores del vector VEC0, imprimir mediante un loop el doble de dichos valores
  • Mediante un loop que itere sobre una de las columnas “string” del dataframe DFRAME, imprimir una variable que combine 3 columnas de dicho dataframe
  • Reescribir el VEC1 del DATAFRAME para que sus elementos sean:
    • El Doble de VEC_0, cuando éste sea mayor a 2
    • Iguales a VEC_0, para el resto de los casos
  • Crear una función llamada HolaMundo que imprima el texto “Hola mundo”
  • Crear una función que devuelva la sumatoria de los números enteros comprendidos entre 1 y un parámetro x a definir

  • Levantar la base Individual del 1er trimestre de 2017, de la EPH
  • Guardar la base como un archivo de extensión .RDS
  • Volver a levantar la base, pero como .RDS y asignarla con el nombre BaseRDS ¿tarda más o menos?
  • Levantar del Excel llamado CANASTAS que se encuentra en la carpeta de Fuentes, la hoja “CBT” y definirla como HojaCBT. Pueden usar la función:
    • read.xlsx de la librería xlsx
    • read_excel de la librería readxl
  • Levantar el mismo Excel, utilizando un Objeto que contenga el directorio del archivo a levantar.
    • Chequear con dir.exist() que lo creamos bien (¿no funcionó? pista: /\)
  • Ejecutar los siguientes comandos:

install.packages(rstudioapi) # SOLO LA PRIMERA VEZ

script.dir <- paste0(dirname(rstudioapi::getActiveDocumentContext()$path),"/")

script.dir

bases.dir  <-  paste0(dirname(script.dir),"/Fuentes/")

bases.dir

dir.create(bases.dir)

resultados.dir <- paste0(dirname(script.dir),"/Resultados/")

resultados.dir

dir.create(resultados.dir)

dir.exists(bases.dir)

¿qué sucedió? Explicar que realiza cada una de las líneas.


  1. Más allá de los comandos elementales, comandos más sofisticados tienen muchas versiones, y algunas quedan en desuso en el tiempo.

  2. veremos que existen ciertas excepciones con algunos paquetes más adelante.

LS0tDQp0aXRsZTogIkNsYXNlIDEuIEludHJvZHVjY2nDs24iDQphdXRob3I6ICJHdWlkbyBXZWtzbGVyIHkgRGllZ28gS296bG93c2tpIg0KZGF0ZTogIjYgZGUgb2N0dWJyZSAyMDE3Ig0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KDQotLS0NCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RiwgY29tbWVudD0iPiIsIGhpZ2hsaWdodD1ULCBpbmNsdWRlPUZBTFNFLCBzdHJpcC53aGl0ZT1UfQ0Kcm0obGlzdD1scygpKQ0KYGBgDQoNCg0KIyBEZXNjcmlwY2nDs24gZGVsIHByb2dyYW1hICAgICAgICANCg0KDQohW2h0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL10oUmxvZ28ucG5nKQ0KDQojIyDCv1F1ZSBlcyBSPw0KDQotIExlbmd1YWplIGRlIEFuw6FsaXNpcyBkZSBEYXRvcw0KLSBTb2Z0d2FyZSBMaWJyZQ0KLSBTaW50YXhpcyBCw6FzaWNhOiBSIGJhc2UgDQotIFNpbnRheGlzIGluY3JlbWVudGFsW14xXTogRWwgbGVuZ3VhamUgc2UgdmEgYW1wbGlhbmRvIHBvciBhcG9ydGVzIGRlIFVuaXZlcnNpZGFkZXMsIGludmVzdGlnYWRvcmVzIHkgZW1wcmVzYXMgcHJpdmFkYXMsIG9yZ2FuaXphZG9zIGVuIGxpYnJlcsOtYXMgKG8gcGFxdWV0ZXMpDQotIF9Db211bmlkYWRfIHdlYiBtdXkgZ3JhbmRlIHBhcmEgcmVhbGl6YXIgcHJlZ3VudGFzIHkgZGVzcGVqYXIgZHVkYXMuDQoNClteMV06IE3DoXMgYWxsw6EgZGUgbG9zIGNvbWFuZG9zIGVsZW1lbnRhbGVzLCBjb21hbmRvcyBtw6FzIHNvZmlzdGljYWRvcyB0aWVuZW4gbXVjaGFzIHZlcnNpb25lcywgeSBhbGd1bmFzIHF1ZWRhbiBlbiBkZXN1c28gZW4gZWwgdGllbXBvLg0KDQohW2Z1ZW50ZTogaHR0cHM6Ly9naXN0LmdpdGh1Yi5jb20vZGFyb2N6aWcvM2NmMDZkNmRiNGJlMmJiZTMzNjhdKG51bWJlci1vZi1zdWJtaXR0ZWQtcGFja2FnZXMtdG8tQ1JBTi5wbmcpDQoNCg0KIVtodHRwczovL3d3dy5yc3R1ZGlvLmNvbS9dKFJTdHVkaW9sb2dvLnBuZykNCg0KRWwgX2VudG9ybm9fIG3DoXMgY8OzbW9kbyBwYXJhIHV0aWxpemFyIGVsIF9sZW5ndWFqZV8gX19SX18gZXMgZWwgX3Byb2dyYW1hXyBfX1Igc3R1ZGlvX18NCg0KLSBSc3R1ZGlvIGVzIHVuYSBlbXByZXNhIHF1ZSBwcm9kdWNlIHByb2R1Y3RvcyBhc29jaWFkb3MgYWwgbGVuZ3VhamUgUiwgY29tbyBlbCBwcm9ncmFtYSBzb2JyZSBlbCBxdWUgY29ycmVtb3MgbG9zIGNvbWFuZG9zLCB5IGV4dGVuc2lvbmVzIGRlbCBsZW5ndWFqZSAobGlicmVyw61hcykuDQogDQotIEVsIHByb2dyYW1hIGVzIF9ncmF0dWl0b18geSBzZSBwdWVkZSBiYWphciBkZSBsYSANCltww6FnaW5hIG9maWNpYWxdKGh0dHBzOi8vd3d3LnJzdHVkaW8uY29tLykgDQoNCg0KIVtQYW50YWxsYSBSc3R1ZGlvXShQYW50YWxsYSBSc3R1ZGlvLnBuZykNCg0KDQojIyBEaWZlcmVuY2lhcyBjb24gU1RBVEEgeSBTUFNTDQoNCi0gR3JhdHVpdG8NCi0gw5puaWNhbWVudGUgZnVuY2lvbmEgcG9yIGzDrW5lYXMgZGUgY8OzZGlnbyAoTm8gaGF5IGJvdG9uZXMgcGFyYSBlamVjdXRhciBjb21hbmRvcykNCi0gUG9zaWJpbGl0YSB0cmFiYWphciBjb24gbcO6bHRpcGxlcyBiYXNlcyBkZSBtaWNyb2RhdG9zIGFsIG1pc21vIHRpZW1wbywgc2luIG1heW9yIGRpZmljdWx0YWQgKE5vIHJlcXVpZXJlIGFicmlyIGNhZGEgYmFzZSwgdHJhYmFqYXJsYSBwb3Igc2VwYXJhZG8geSBsdWVnbyBjZXJyYXJsYSkgDQotIE3DoXMgcG90ZW50ZQ0KICAgIC0gVG90YWxtZW50ZSBhdXRvbWF0aXphYmxlDQogICAgLSBBcG9ydGVzIGRlIHVzdWFyaW8gDQogICAgLSBFeHRlbnNpYmxlIGEgb3Ryb3MgbGVuZ3VhamVzIHkgdXNvcyAoZXN0YSBwcmVzZW50YWNpw7NuKQ0KLSBNw6FzIHZlbG96Og0KDQoNCiFbZnVlbnRlOiBodHRwczovL2dpdGh1Yi5jb20vbWF0dGhpZXVnb21lei9iZW5jaG1hcmstc3RhdGEtci9ibG9iL21hc3Rlci9vdXRwdXQvMWU3LnBuZ10oc3RhdGFSLnBuZykNCg0KDQoNCiMgTMOzZ2ljYSBzaW50w6FjdGljYS4NCg0KIyMgRGVmaW5pY2nDs24gZGUgb2JqZXRvcw0KDQpFbCBvcGVyYWRvciBfX2BgYDwtYGBgX18gc2lydmUgcGFyYSBkZWZpbmlyIHVuIGVsZW1lbnRvLiAgX19BIGxhIGl6cXVpZXJkYV9fIGRlbCBfX2BgYDwtYGBgX18gZGViZSB1YmljYXJzZSBlbCBub21icmUgcXVlIHRvbWFyw6EgZWwgZWxlbWVudG8gYSBjcmVhci4gX19EZWwgbGFkbyBkZXJlY2hvX18gZGViZSBpciBsYSBkZWZpbmljacOzbiBkZWwgbWlzbW8NCg0KDQpgYGB7cn0NCkEgPC0gMQ0KYGBgDQoNCkFsIGRlZmluaXIgdW4gZWxlbWVudG8sIGVsIG1pc21vIHF1ZWRhIGd1YXJkYWRvIGVuIGVsIGFtYmllbnRlIGRlbCBwcm9ncmFtYSwgeSBwb2Ryw6Egc2VyIHV0aWxpemFkbyBwb3N0ZXJpb3JtZW50ZSBwYXJhIG9ic2VydmFyIHN1IGNvbnRlbmlkbyBvIHBhcmEgcmVhbGl6YXIgdW5hIG9wZXJhY2nDs24gY29uIGVsIG1pc21vDQoNCmBgYHtyfQ0KQSANCmBgYA0KDQpBbCBjb3JyZXIgdW5hIGxpbmVhIGNvbiBlbCBub21icmUgZGVsIG9iamV0bywgbGEgY29uc29sYSBkZWwgcHJvZ3JhbWEgbm9zIG11ZXN0cmEgc3UgY29udGVuaWRvLiBFbnRyZSBDb3JjaGV0ZXMgT2JzZXJ2YW1vcyBlbCBuw7ptZXJvIGRlIG9yZGVuIGRlbCBlbGVtZW50byBlbiBjdWVzdGnDs24NCg0KRWwgb3BlcmFkb3IgX19gYGA9YGBgX18gZXMgX19lcXVpdmFsZW50ZV9fIGEgX19gYGA8LWBgYF9fLCBwZXJvIGVuIGxhIHByw6FjdGljYSBubyBzZSB1dGlsaXphIHBhcmEgbGEgZGVmaW5pY2nDs24gZGUgb2JqZXRvcy4gICANCg0KYGBge3J9DQpCID0gMg0KQg0KYGBgDQoNCl9fYGBgPC1gYGBfXyBlcyB1biBvcGVyYWRvciBfX1VuaWRpcmVjY2lvbmFsX18sIGVzIGRlY2lyIHF1ZTogICAgIA0KYGBgQSA8LSBCYGBgIGltcGxpY2EgcXVlIF9fQV9fIHZhIHRvbWFyIGNvbW8gdmFsb3IgZWwgY29udGVuaWRvIGRlbCBvYmpldG8gX19CX18sIHkgbm8gYWwgcmV2w6lzLg0KYGBge3J9DQpBIDwtIEINCkEgICAjQWhvcmEgQSB0b21hIGVsIHZhbG9yIGRlIEIsIHkgQiBjb250aW51YSBjb25zZXJ2YW5kbyBlbCBtaXNtbyB2YWxvcg0KQg0KYGBgDQoNCg0KIyMgUiBiYXNlDQoNCkNvbiBfUiBiYXNlXyBub3MgcmVmZXJpbW9zIGEgbG9zIGNvbWFuZG9zIGLDoXNpY29zIHF1ZSB2aWVuZW4gaW5jb3Jwb3JhZG9zIGVuIGVsIFIsIHNpbiBuZWNlc2lkYWQgZGUgY2FyZ2FyIGxpYnJlcsOtYXMuIA0KDQojIyBPcGVyYWRvcmVzIGzDs2dpY29zOiANCg0KLSAkPiQNCi0gJD49JA0KLSAkPCQNCi0gJDw9JA0KLSAkPT0kDQotICQhPSQNCg0KYGBge3J9DQpBID0gMQ0KQiA9IDINCg0KQSA+ICBCDQpBID49IEINCkEgPCAgQg0KQSA8PSBCDQpBID09IEINCkEgIT0gQg0KDQpDIDwtIEEgIT0gQg0KQw0KYGBgDQoNCkNvbW8gbXVlc3RyYSBlbCDDumx0aW1vIGVqZW1wbG8sIGVsIHJlc3VsdGFkbyBkZSB1bmEgb3BlcmFjacOzbiBsw7NnaWNhIHB1ZWRlIGFsbWFjZW5hcnNlIGNvbW8gZWwgdmFsb3IgZGUgdW4gb2JqZXRvLg0KDQojIyBPcGVyYWRvcmVzIGFyaXRtw6l0aWNvczoNCg0KYGBge3J9DQojc3VtYQ0KQSA8LSA1KzYNCkENCiNSZXN0YQ0KQiA8LSA2LTgNCkINCiNjb2NpZW50ZQ0KQyA8LSA2LzIuNQ0KQw0KI211bHRpcGxpY2FjaW9uDQpEIDwtIDYqMi41DQpEDQpgYGANCiMjIENhcmFjdGVyZXMgZXNwZWNpYWxlcw0KDQotIFIgZXMgc2Vuc2libGUgYSBtYXnDunNjdWxhcyB5IG1pbsO6c2N1bGFzLCB0YW50byBwYXJhIGxvcyBub21icmVzIGRlIGxhcyB2YXJpYWJsZXMsIGNvbW8gcGFyYSBsYXMgZnVuY2lvbmVzIHkgcGFyw6FtZXRyb3MuDQotIExvcyBfX2VzcGFjaW9zIGVuIGJsYW5jb19fIHkgbG9zIF9fY2FycmlhZ2UgcmV0dXJuX18gKF9lbnRlcl8pIG5vIHNvbiBjb25zaWRlcmFkb3MgcG9yIGVsIGxlbmd1YWplLiBMb3MgcG9kZW1vcyBhcHJvdmVjaGFyIHBhcmEgZW1wcm9saWphciBlbCBjw7NkaWdvIHkgcXVlIGxhIGxlY3R1cmEgc2VhIG3DoXMgc2ltcGxlW14yXS4NCg0KW14yXTogdmVyZW1vcyBxdWUgZXhpc3RlbiBjaWVydGFzIGV4Y2VwY2lvbmVzIGNvbiBhbGd1bm9zIHBhcXVldGVzIG3DoXMgYWRlbGFudGUuIA0KDQotIEVsIF9fbnVtZXJhbF9fIGBgYCNgYGAgc2UgdXRpbGl6YSBwYXJhIGhhY2VyIGNvbWVudGFyaW9zLiBUb2RvIGxvIHF1ZSBzZSBlc2NyaWJlIGRlc3B1w6lzIGRlbCAjIG5vIGVzIGludGVycHJldGFkbyBwb3IgUi4gU2UgZGViZSB1dGlsaXphciB1biAjIHBvciBjYWRhIGzDrW5lYSBkZSBjw7NkaWdvIHF1ZSBzZSBkZXNlYSBhbnVsYXINCg0KLSBMb3MgX19jb3JjaGV0ZXNfXyBgYGBbXWBgYCBzZSB1dGlsaXphbiBwYXJhIGFjY2VkZXIgYSB1biBvYmpldG86DQogICAgLSBlbiB1biB2ZWN0b3JbbsKwIG9yZGVuXQ0KICAgIC0gZW4gdW5hIG1hdHJpeltmaWxhLCBjb2x1bW5hXQ0KICAgIC0gZW4gdW5hIGxpc3RhW27CsCBlbGVtZW50b10NCi0gZWwgc2lnbm8gX18kX18gdGFtYmnDqW4gZXMgdW4gbcOpdG9kbyBkZSBhY2Nlc28sIHF1ZSBwZXJtaXRlIGxsYW1hciBhbCBlbGVtZW50byBwb3Igc3Ugbm9tYnJlLCBlbiBkYXRhZnJhbWVzIHkgbGlzdGFzLg0KDQotIExvcyBfX3BhcsOpbnRlc2lzX19gYGAoKWBgYCBzZSB1dGlsaXphbiBlbiBsYXMgZnVuY2lvbmVzIHBhcmEgZGVmaW5pciBsb3MgcGFyw6FtZXRyb3MuDQoNCi0gTGFzIF9fY29tYXNfXyBgYGAsIGBgYCAgc2UgdXRpbGl6YW4gcGFyYSBzZXBhcmFyIGxvcyBlbGVtZW50b3MuIA0KDQpFamVtcGxvOiBzaSBxdWVyZW1vcyBkZWZpbmlyIGFsIGVsZW1lbnRvIGRlIGxhIGZpbGEgMiB5IGNvbHVtbmEgMyBkZSB1bmEgX3RhYmxhXyAgY29tbyBlbCByZXN1bHRhZG8gZGUgdW5hIF9mdW5jaW9uXyBjb24gcGFyw6FtZXRyb3MgQT1hLCBCPWIgeSBDPWMsIGRpcmVtb3M6DQoNCmBgYA0KdGFibGFbMiwzXSA8LSBmdW5jaW9uKEE9YSwgQj1iLCBDPWMpDQpgYGANCg0KIyMgT2JqZXRvczoNCg0KLSBWYWxvcg0KLSBWZWN0b3INCi0gTWF0cml6DQotIERhdGEgRnJhbWUNCi0gTGlzdGENCg0KDQoNCiMjIyBWYWxvcmVzDQpMb3MgdmFsb3JlcyB0aWVuZW4gZGlzdGludG9zIF90aXBvc186ICAgDQoNCl9fTnVtZXJpY19fDQpgYGB7cn0NCkEgPC0gIDENCmNsYXNzKEEpDQpgYGANCg0KDQoNCl9fQ2hhcmFjdGVyX18NCmBgYHtyfQ0KQSA8LSAgcGFzdGUoJ1NveScsICd1bmEnLCAnY29uY2F0ZW5hY2nDs24nLCAnZGUnLCAnY2FyYWN0ZXJlcycsIHNlcCA9ICIgIikNCkENCmNsYXNzKEEpDQpgYGANCg0KDQoNCl9fRmFjdG9yX18NCmBgYHtyfQ0KQSA8LSBmYWN0b3IoIlNveSB1biBmYWN0b3IsIGNvbiBuaXZlbGVzIGZpam9zIikNCmNsYXNzKEEpDQpgYGANCg0KTGEgZGlmZXJlbmNpYSBlbnRyZSB1biBfY2hhcmFjdGVyXyB5IHVuIF9mYWN0b3JfIGVzIHF1ZSBlbCDDumx0aW1vIHRpZW5lIHNvbG8gYWxndW5vcyB2YWxvcmVzIHBlcm1pdGlkb3MgKGxldmVscyksIHkgc2UgbGUgcHVlZGUgZGFyIHVuIG9yZGVuDQoNCg0KDQpfX0RhdGVfXw0KYGBge3J9DQpBIDwtIGFzLkRhdGUoIjIwMTctMDEtMDEiKQ0KY2xhc3MoQSkNCmBgYA0KDQoNCg0KIyMjIFZlY3RvcmVzDQoNClBhcmEgY3JlYXIgdW4gX192ZWN0b3JfXyB1dGlsaXphbW9zIGVsIGNvbWFuZG8gYGBgYygpYGBgLCBkZSBjb21iaW5hci4gUHVlZGUgdGVuZXIgdmFyaWFibGVzIF9fZGUgY3VhbHF1aWVyIHRpcG9fXw0KDQpgYGB7cn0NCkMgPC0gYygxLCAzLCA0KQ0KQw0KYGBgDQoNCnN1bWFybGUgIDIgYSBjYWRhIGVsZW1lbnRvIGRlbCBfX3ZlY3Rvcl9fIGFudGVyaW9yDQoNCmBgYHtyfQ0KQyA8LSBDICsgMg0KQw0KYGBgDQoNCnN1bWFybGUgIDEgYWwgcHJpbWVyIGVsZW1lbnRvLCAyIGFsIHNlZ3VuZG8sIHkgMyBhbCB0ZXJjZXIgZWxlbWVudG8gZGVsIF9fdmVjdG9yX18gYW50ZXJpb3INCmBgYHtyfQ0KRCA8LSBDICsgMTozICNlc3RvIGVzIGVxdWl2YWxlbnRlIGEgaGFjZXIgMysxLCA1KzIsIDYrOSANCkQNCmBgYA0KDQpgYGAxOjNgYGAgc2lnbmlmaWNhIHF1ZSBxdWVyZW1vcyB0b2RvcyBsb3MgbsO6bWVyb3MgZW50ZXJvcyBkZXNkZSAxIGhhc3RhIDMuIA0KDQpjcmVhciB1biBfX3ZlY3Rvcl9fIHF1ZSBjb250ZW5nYSBsYXMgcGFsYWJyYXM6IEluZ3Jlc28sIFZhcmlhY2nDs24sIEluZGljZQ0KYGBge3J9DQpFIDwtIGMoIkNhcmxvcyIsIkZlZGVyaWNvIiwiUGVkcm8iKQ0KRQ0KYGBgDQoNCnBhcmEgYWNjZWRlciBhIGFsZ8O6biBlbGVtZW50byBkZWwgdmVjdG9yLCBwb2RlbW9zIGJ1c2NhcmxvIHBvciBzdSBuw7ptZXJvIGRlIG9yZGVuLCBlbnRyZSBgYGBbIF1gYGANCg0KYGBge3J9DQplbGVtZW50bzIgPC0gRVsyXQ0KZWxlbWVudG8yDQpgYGANCg0KcGFyYSBfX2JvcnJhcl9fIHVuIG9iamV0bywgdXRpbGl6YW1vcyBlbCBjb21hbmRvIF9gYGBybSgpYGBgXw0KDQpgYGB7ciBlcnJvcj1UUlVFfQ0Kcm0oZWxlbWVudG8yKQ0KZWxlbWVudG8yDQpgYGANCg0KVGFtYmnDqW4gcG9kZW1vcyBjYW1iaWFyIGVsIHRleHRvIGRlbCBzZWd1bmRvIGVsZW1lbnRvIGRlIEUsIHBvciBlbCB0ZXh0byAidmFyIg0KDQpgYGB7cn0NCkVbMl0gPC0gIlBhYmxvIg0KRQ0KYGBgDQoNCiMjIyBEYXRhIEZyYW1lcw0KDQpVbiBEYXRhIEZyYW1lIGVzIHVuYSB0YWJsYSBkZSBkYXRvcywgZG9uZGUgY2FkYSBjb2x1bW5hIHJlcHJlc2VudGEgdW5hIHZhcmlhYmxlLCB5IGNhZGEgZmlsYSB1bmEgb2JzZXJ2YWNpw7NuLiBTb24gZWwgZXF1aXZhbGVudGUgYSBsYXMgdGFibGFzIGRlIF9TVEFUQV8geSBfU1BTU18NCg0KRXN0ZSBvYmpldG8gc3VlbGUgc2VyIGNlbnRyYWwgZW4gZWwgcHJvY2VzbyBkZSB0cmFiYWpvLCB5IHN1ZWxlIHNlciBsYSBmb3JtYSBlbiBxdWUgc2UgY2FyZ2FuIGRhdG9zIGV4dGVybm9zLCBhc8OtIGNvbW8gbGEgbWF5b3LDrWEgZGUgbG9zIGVsZW1lbnRvcyBpbnRlcm1lZGlvcywgaGFzdGEgYXF1ZWxsbyBxdWUgZXhwb3J0ZW1vcyANCg0KVGFtYmnDqW4gU2UgcHVlZGUgY3JlYXIgY29tbyBsYSBjb21iaW5hY2nDs24gZGUgTiB2ZWN0b3JlcyBkZSBpZ3VhbCB0YW1hw7FvLiBQb3IgZWplbXBsbywgdG9tYW1vcyBhbGd1bm9zIHZhbG9yZXMgZGVsIFtJbmRpY2UgZGUgc2FsYXJpb3NdKGh0dHA6Ly93d3cuaW5kZWMuZ29iLmFyL2JhamFyQ3VhZHJvRXN0YWRpc3RpY28uYXNwP2lkYz00MDIwQjMzNDQwNjA5NDYyNjU0NTQyQkQwQkMzMjBGMTUyM0RBMERDNTJDMzk2MjAxREI0REQ1ODYxRkZFREM5QUQxNDM2NjgxQUM4NDE3OSkNCg0KYGBge3J9DQpJTkRJQ0UgIDwtIGMoMTAwLCAgIDEwMCwgICAxMDAsDQogICAgICAgICAgICAgMTAxLjgsIDEwMS4yLCAxMDAuNzMsDQogICAgICAgICAgICAgMTAyLjksIDEwMi40LCAxMDMuMikNCg0KRkVDSEEgIDwtICBjKCJPY3QtMTYiLCAiT2N0LTE2IiwgIk9jdC0xNiIsDQogICAgICAgICAgICAgIk5vdi0xNiIsICJOb3YtMTYiLCAiTm92LTE2IiwNCiAgICAgICAgICAgICAiRGljLTE2IiwgIkRpYy0xNiIsICJEaWMtMTYiKQ0KDQoNCkdSVVBPICA8LSAgYygiUHJpdmFkb19SZWdpc3RyYWRvIiwiUMO6YmxpY28iLCJQcml2YWRvX05vX1JlZ2lzdHJhZG8iLA0KICAgICAgICAgICAgICJQcml2YWRvX1JlZ2lzdHJhZG8iLCJQw7pibGljbyIsIlByaXZhZG9fTm9fUmVnaXN0cmFkbyIsDQogICAgICAgICAgICAgIlByaXZhZG9fUmVnaXN0cmFkbyIsIlDDumJsaWNvIiwiUHJpdmFkb19Ob19SZWdpc3RyYWRvIikNCiAgICAgICAgICAgICANCg0KRGF0b3MgPC0gZGF0YS5mcmFtZShJTkRJQ0UsIEZFQ0hBLCBHUlVQTykNCkRhdG9zDQpgYGANCg0KVGFsIGNvbW8gZW4gdW4gX192ZWN0b3JfXyBzZSB1YmljYSBhIGxvcyBlbGVtZW50b3MgbWVkaWFudGUgYGBgWyBdYGBgLCBlbiB1biBfX2RhdGFmcmFtZV9fIHNlIG9idGllbmVuIHN1cyBlbGVtZW50b3MgZGUgbGEgZm9ybWEgX19gYGBbZmlsYSwgY29sdW1uYV1gYGBfXy4NCg0KT3RyYSBvcGNpw7NuIGVzIHNlbGVjY2lvbmFyIGxhIGNvbHVtbmEsIG1lZGlhbnRlIGVsIG9wZXJhZG9yIF9fYGBgJGBgYF9fLCB5IGx1ZWdvIHNlbGVjY2lvbmFyIGRlbnRybyBkZSBlc2EgY29sdW1uYSwgcG9yIGVsIG7Dum1lcm8gZGUgb3JkZW4uDQoNCmBgYHtyfQ0KRGF0b3MkRkVDSEENCkRhdG9zWzMsMl0NCkRhdG9zJEZFQ0hBWzNdDQpgYGANCg0Kwr9xdWUgcGFzYSBzaSBoYWNlbW9zIGBgYERhdG9zJEZFQ0hBWzMsMl1gYGAgPw0KDQpgYGB7ciBlcnJvcj1UUlVFfQ0KRGF0b3MkRkVDSEFbMywyXQ0KYGBgDQpOw7N0ZXNlIHF1ZSBlbCDDumx0aW1vIGNvbWFuZG8gdGllbmUgdW4gbsO6bWVybyBpbmNvcnJlY3RvIGRlIGRpbWVuc2lvbmVzLCBwb3JxdWUgZXN0YW1vcyByZWZpcmllbmRvbm9zIDIgdmVjZXMgYSBsYSBjb2x1bW5hIEZFQ0hBLg0KDQojIyMgTGlzdGFzDQoNCkNvbnRpZW5lbiB1bmEgY29uY2F0ZW5hY2nDs24gZGUgb2JqZXRvcyBkZSBjdWFscXVpZXIgdGlwby4gQXPDrSBjb21vIHVuIHZlY3RvciBjb250aWVuZSB2YWxvcmVzLCB1biBkYXRhZnJhbWUgY29udGllbmUgdmVjdG9yZXMsIHVuYSBsaXN0YSBwdWVkZSBjb250ZW5lciBkYXRhZnJhbWVzLCBwZXJvIHRhbWJpw6luIHZlY3RvcmVzLCBvIHZhbG9yZXMsIHkgX3RvZG8gZWxsbyBhIGxhIHZlel8NCg0KYGBge3J9DQpzdXBlcmxpc3RhIDwtIGxpc3QoQSxCLEMsRCxFLEZFQ0hBLCBERiA9IERhdG9zLCBJTkRJQ0UsIEdSVVBPKQ0Kc3VwZXJsaXN0YQ0KYGBgDQoNClBhcmEgYWNjZWRlciB1biBlbGVtZW50byBkZSB1bmEgbGlzdGEsIHBvZGVtb3MgdXRpbGl6YXIgZWwgb3BlcmFkb3IgX19gYGAkYGBgX18sIHF1ZSBzZSBwdWVkZSB1c2FyIGEgc3UgdmV6IGRlIGZvcm1hIGl0ZXJhdGl2YSANCg0KYGBge3J9DQpzdXBlcmxpc3RhJERGJEZFQ0hBWzJdDQpgYGANCg0KIyMgTG9vcHMsIGNvbmRpY2lvbmFsZXMgeSBmdW5jaW9uZXMNCg0KVW4gX19sb29wX18gZXMgdW5hIGVzdHJ1Y3R1cmEgZGUgY8OzZGlnbyBxdWUgbm9zIHBlcm1pdGUgcmVjb3JyZXIgaXRlcmF0aXZhbWVudGUgdW4gY29uanVudG8gZGUgY29tYW5kb3MsIHZhcmlhbmRvIGFsZ8O6biBlbGVtZW50by4gUG9yIGVqZW1wbG86DQoNCmBgYHtyfQ0KZm9yKGkgaW4gMToxMCl7DQogIEN1YWRyYWRvczwtIGleMg0KICBwcmludChDdWFkcmFkb3MpDQp9DQoNCmBgYA0KDQplc3RvIHNlIGxlZSBjb21vIDogUGFyYSBpLCBxdWUgdG9tYSBsb3MgdmFsb3JlcyBkZSAxIGEgMTA6IGltcHJpbcOtIGkuDQoNClRhbWJpw6luIHNlIHB1ZWRlIHRvbWFyIHVuYSBsaXN0YSBkZSB2YWxvcmVzIGN1YWxlc3F1aWVyYS4gUG9yIGVqZW1wbG8gcmV1dGlsaXphciBkYXRvcyBkZSB1biBkYXRhZnJhbWU6DQoNCmBgYHtyfQ0KRGF0b3MNCnVuaXF1ZShEYXRvcyRHUlVQTykNCmZvcih2YXJpYWJsZSBpbiB1bmlxdWUoRGF0b3MkR1JVUE8pKXsNCiAgcHJpbnQoRGF0b3NbRGF0b3MkR1JVUE8gPT0gdmFyaWFibGUsXSkNCn0NCg0KYGBgDQoNCkxhcyBfX2VzdHJ1Y3R1cmFzIGNvbmRpY29uYWxlc19fIG5vcyBwZXJtaXRlcyBlamVjdXRhciBjw7NkaWdvIGRlIGZvcm1hIGNvbmRpY2lvbmFsIGEgcXVlIHNlIGN1bXBsYSBkZXRlcm1pbmFkYSBjb25kaWNpw7NuLiANCg0KDQpgYGB7cn0NCkENCnJlc3VsdGFkbyA8LSBpZmVsc2UodGVzdCA9IGNsYXNzKEEpID09ICJjaGFyYWN0ZXIiLCB5ZXMgPSAgcGFzdGUwKCJFc3RvIGVzIHVuIGNhcmFjdGVyIiksDQogICAgICAgbm8gPSBwYXN0ZTAoIkVzdG8gbm8gZXJhIHVuIGNhcmFjdGVyIikpDQoNCnJlc3VsdGFkbw0KDQpgYGANCg0KT3RyYSBmdW5jacOzbiBwYXJhIGNvbmRpY2lvbmFyIGxhIGVqZWN1Y2nDs24gZGUgdW5hIHBvcmNpw7NuIGRlbCBjw7NkaWdvIGVzIGBgYGlmKGNvbmRpY2lvbil7Y29kaWdvIGEgZWplY3V0YXIgc2kgc2UgY3VtcGxlIGxhIGNvbmRpY2nDs259YGBgDQoNCkxhcyBfX2Z1bmNpb25lc19fIG5vcyBwZXJtaXRlbiBhdXRvbWF0aXphciB0b2RhcyBhcXVlbGxhcyBwYXJ0ZXMgZGVsIGPDs2RpZ28gcXVlIHNlIHJlcGl0ZW4gbXVjaG8uIFVuYSB2ZXogZGlzZcOxYWRhcywgZnVuY2lvbmFuIGlndWFsIHF1ZSBjdWFscXVpZXIgY29tYW5kby4gTGEgZmFjaWxpZGFkIHBhcmEgY3JlYXIgbGFzIGZ1bmNpb25lcyBlcyBlbiBidWVuYSBtZWRpZGEgbGEgZXhwbGljYWNpw7NuIGRlIHF1ZSBoYXlhIHRhbnRhcyBjb250cmlidWNpb25lcyBkZSB1c3VhcmlvcyBhIGxhIGV4cGFuc2nDs24gZGVsIGxlbmd1YWplLg0KDQpgYGB7cn0NCmZ1bmNpb25fcHJ1ZWJhIDwtIGZ1bmN0aW9uKGEsYikgew0KICBwcmludChwYXN0ZShhLCBiLCBzZXAgPSAiICIpKQ0KICANCn0NCmZ1bmNpb25fcHJ1ZWJhKGEgPSAic295IGxhIHByaW1lcmEgcGFydGUgZGUgdW4gc3RyaW5nLiIsIGIgPSAiWSB5byBsYSBzZWd1bmRhIHBhcnRlIikNCg0KYGBgDQoNClRhbWJpw6luIHBvZGVtb3MgYXNpZ25hciB1biB2YWxvciBwb3IgZGVmYXVsdC4NCg0KYGBge3J9DQpmdW5jaW9uX3BydWViYSA8LSBmdW5jdGlvbihhID0gIlNveSB1bmEgcHJpbWVyYSBwYXJ0ZSBkZWZhdWx0LiIsYikgew0KICBwcmludChwYXN0ZShhLCBiLCBzZXAgPSAiICIpKQ0KICANCn0NCmZ1bmNpb25fcHJ1ZWJhKGIgPSAiICBBIG1pIG5vIG1lIGRlZmF1bHRlYXMiKQ0KDQpgYGANCg0KTGFzIGZ1bmNpb25lcyBxdWUgY3JlYW1vcyBub3NvdHJvcyB2aXZlbiBkZW50cm8gZGVsIHNjcmlwdCBkb25kZSBzZSBsYXMgZGVmaW5lLiBFcyBkZWNpciwgZXMgbmVjZXNhcmlvIHZvbHZlciBhIGNvcnJlciBsYSBkZWZpbmljacOzbiBjYWRhIHZleiBxdWUgbGEgcXVlcmVtb3MgdXRpbGl6YXIuIA0KDQpWYWxlIG1lbmNpb25hciBxdWUgX19sbyBxdWUgb2N1cnJlIGVuIHVuYSBmdW5jacOzbiwgcXVlZGEgZW4gbGEgZnVuY2nDs25fXyBleGNlcHRvIHF1ZSBleHBsw61jaXRhbWVudGUgcGlkYW1vcyBxdWUgZGV2dWVsdmEgZWwgcmVzdWx0YWRvLCBjb24gZWwgY29tYW5kbyBgYGBwcmludCgpYGBgIG8gYGBgcmV0dXJuKClgYGANCg0KIyBMZWN0dXJhIHkgZXNjcml0dXJhIGRlIGFyY2hpdm9zDQoNClIgdGllbmUgZm9ybWF0b3MgZGUgYXJjaGl2b3MgcHJvcGlvczoNCg0KLSBSZGF0YQ0KLSBSRFMNCg0KIyMgUkRhdGENCmBgYHtyfQ0KeCA8LSAxOjE1DQp5IDwtIGxpc3QoYSA9IDEsIGIgPSBUUlVFLCBjID0gIm9vcHMiKQ0KDQojUGFyYSBndWFyZGFyDQpzYXZlKHgsIHksIGZpbGUgPSAieHkuUkRhdGEiKQ0KDQojUGFyYSBsZWVyDQpsb2FkKCd4eS5SRGF0YScpDQpgYGANCg0KTG9zIGFyY2hpdm9zIGRlIHRpcG8gX19SRGF0YV9fIHBlcm1pdGVuIGdyYWJhciB1bmEgX2ltYWdlbl8gZGUgdG9kb3MgbG9zIG9iamV0b3MgUiBxdWUgcXVlcnJhbW9zLg0KDQojIyBfX1JEU19fDQpgYGB7cn0NCngNCnNhdmVSRFMoeCwgInguUkRTIikNCg0KWiA8LSByZWFkUkRTKCJ4LlJEUyIpDQpaDQpgYGANCg0KTG9zIGFyY2hpdm9zIGRlIHRpcG8gX19SRFNfXyBubyBndWFyZGFuIGVsIG5vbWJyZSBkZWwgb2JqZXRvLCBwb3IgbG8gcXVlIHBvZGVtb3Mgbm9tYnJhcmxvcyBjdWFuZG8gbG9zIGNhcmdhbW9zIChhY29uc2VqYWJsZSkNCg0KIyMgQXJjaGl2b3MgZGUgb3Ryb3MgZm9ybWF0b3MNCg0KSGF5IF9fbXVjaGFzX18gZnVuY2lvbmVzIHBhcmEgbGVlciBhcmNoaXZvcyBkZSB0aXBvIF8udHh0XyB5IF8uY3N2Xy4gTGEgbWF5b3LDrWEgc8OzbG8gY2FtYmlhIGxvcyBwYXLDoW1ldHJvcyBxdWUgdmllbmVuIHBvciBkZWZhdWx0LiANCg0KRXMgaW1wb3J0YW50ZSB0ZW5lciBlbiBjdWVudGE6DQoNCi0gZW5jYWJlemFkbw0KLSBkZWxpbWl0YWRvciAoYGBgLGBgYCwgdGFiLCBgYGA7YGBgKQ0KLSBzZXBhcmFkb3IgZGVjaW1hbCANCg0KDQpgYGAgDQpkYXRhZnJhbWUgPC0gcmVhZC5kZWxpbShmaWxlLCBoZWFkZXIgPSBUUlVFLCBzZXAgPSAiXHQiLCBxdW90ZSA9ICJcIiIsIGRlYyA9ICIuIiwgZmlsbCA9IFRSVUUsIGNvbW1lbnQuY2hhciA9ICIiLCAuLi4pIA0KYGBgDQpFamVtcGxvLiBMZXZhbnRhciBsYSBiYXNlIGluZGl2aWR1YWwgZGUgRVBIIGRlbCAxZXIgdHJpbWVzdHJlIDIwMTcNCmBgYHtyfQ0KDQppbmRpdmlkdWFsX3QxMTcgPC0gcmVhZC50YWJsZSgnLy9ORTE4MTA2L1RyYWJham9zL0N1cnNvIFIvRnVlbnRlcy91c3VfaW5kaXZpZHVhbF90MTE3LnR4dCcsc2VwPSI7IiwgZGVjPSIsIiwgaGVhZGVyID0gVFJVRSwgZmlsbCA9IFRSVUUpDQppbmRpdmlkdWFsX3QxMTcNCmBgYA0KDQoNCiANCmFwcm92ZWNoZW1vcyBlc3RhIHByaW1lcmEgdGFibGEgX2VuIHNlcmlvXyBwYXJhIGludHJvZHVjaXIgYWxndW5vcyBjb21hbmRvcyDDunRpbGVzIHBhcmEgdGVuZXIgdW5hIG1pcmFkYSByw6FwaWRhIGRlIGxhIGJhc2UuDQoNCg0KYGBge3J9DQoNCiNWaWV3KGluZGl2aWR1YWxfdDExNykNCm5hbWVzKGluZGl2aWR1YWxfdDExNykNCnN1bW1hcnkoaW5kaXZpZHVhbF90MTE3KVssYyg4LDEwLDMxLDEzMyldDQpoZWFkKGluZGl2aWR1YWxfdDExNylbLDE6NV0NCg0KYGBgDQoNCg0KIyMgRXhjZWwgDQoNClBhcmEgbGVlciBhcmNoaXZvcyBleGNlbCBkZWJlbW9zIHV0aWxpemFyIGxvcyBjb21hbmRvcyBxdWUgdmllbmVuIGNvbiBsYSBsaWJyZXLDrWEgeGxzeA0KDQpgYGB7cn0NCiMgaW5zdGFsbC5wYWNrYWdlcygieGxzeCIpICMgcG9yIMO6bmljYSB2ZXoNCmxpYnJhcnkoeGxzeCkgI2FjdGl2YW1vcyBsYSBsaWJyZXLDrWENCg0KI2NyZWFtb3MgdW5hIHRhYmxhIGN1YWxxdWllcmEgZGUgcHJ1ZWJhDQp4IDwtIDE6MTANCnkgPC0gMTE6MjANCnRhYmxhX2RlX1IgPC0gZGF0YS5mcmFtZSh4LHkpDQoNCiMgZXNjcmliaW1vcyBlbCBhcmNoaXZvDQp3cml0ZS54bHN4KCB4ID0gdGFibGFfZGVfUiwgZmlsZSA9ICJhcmNoaXZvLnhsc3giLHNoZWV0TmFtZSA9ICJob2phIDEiLHJvdy5uYW1lcyA9IEZBTFNFKQ0KDQojbGVlbW9zIGVsIGFyY2hpdm8NCnRhYmxhIDwtIHJlYWQueGxzeChmaWxlID0gImFyY2hpdm8ueGxzeCIsc2hlZXROYW1lID0gImhvamEgMSIpDQp0YWJsYQ0KYGBgDQoNCiMjIFNQU1MsIFNUQVRBLCBTQVMNCg0KUG9kZW1vcyB1dGlsaXphciBsYSBsaWJyZXLDrWEgX2hhdmVuXywgeSBsb3MgY29tYW5kb3M6DQoNCi0gcmVhZF9zcHNzKCkNCi0gcmVhZF9kdGEoKQ0KLSByZWFkX3NhcygpDQoNCiMjIEVuY29kaW5nDQpUYW50byBhIGxhIGhvcmEgZGUgbGVlciB5IGVzY3JpYmlyIGFyY2hpdm9zLCBjb21vIGFsIHRyYWJhamFyIHVuIG1pc21vIHNjcmlwdCBkZXNkZSBkaXN0aW50YXMgY29tcHV0YWRvcmFzLCBkZWJlbW9zIHNlciBjdWlkYWRvc29zIGNvbiBlbCBfZW5jb2RpbmdfIHNldGVhZG8uIEVsIF9lbmNvZGluZ18gZXMgZWwgc2lzdGVtYSBtZWRpYW50ZSBlbCBjdWFsIGVsIHNpc3RlbWEgaW50ZXJwcmV0YSBsb3MgY2FyYWN0ZXJlcyBkZWwgbGVuZ3VhamUgbmF0dXJhbC4gSGF5IG11Y2hvcyBfZW5jb2RpbmdzXyBkaWZlcmVudGVzLCBxdWUgaW50ZXJwcmV0YW4gZGlzdGludG8gYWxndW5vcyBjYXJhY3RlcmVzLCBjb21vIHRpbGRlcyB5IHNpZ25vcyBkZSBwdW50dWFjacOzbi4gICAgIA0KUG9yIGVuZGUsIHNpIGVsIF9lbmNvZGluZ18gc2V0ZWFkbyBubyBlcyBlbCBtaXNtbyBxdWUgZWwgZGUgbnVlc3RybyBzY3JpcHQvdGFibGEgcHVlZGVuIGdlbmVyYXJzZSBlcnJvcmVzLiBFbiBtZWRpZGEgZGUgbG8gcG9zaWJsZSwgYWwgZXNjcmliaXIgbnVlc3Ryb3Mgc2NyaXB0cyBlcyByZWNvbWVuZGFibGUgZXZpdGFyIGVzdG9zIGNhcmFjdGVyZXMuIA0KDQpSIHRpZW5lIHBvciBkZWZhdWx0IGVsIGVuY29kaW5nIF9fIklTTy04ODU5LTEiX18sIHNpbiBlbWJhcmdvIGVsIG3DoXMgaGFiaXR1YWwgZW4gQW3DqXJpY2EgTGF0aW5hIGVzIF9fIlVURi04Il9fLiANCg0KLSAqKkxlY3R1cmEgZGUgYXJjaGl2b3MqKiA6IEFnbHVuYXMgZGUgbGFzIGZ1bmNpb25lcyBkZWwgdGlwbyBfX3JlYWRfdGFibGVfXywgX19yZWFkX3hsc3hfXyBwZXJtaXRlbiBlc3RhYmxlY2VyIGNvbW8gdW5vIGRlIHN1cyBwYXJhbWV0cm9zIGVsIF9lbmNvZGluZ18gZGVzZWFkbyANCi0gKipFbmNvZGluZyB1dGlsaXphZG8gcGFyYSBhYnJpciB1biBzY3JpcHQqKjpGaWxlIC0+IFJlb3BlbiB3aXRoIEVuY29kaW5nDQotICoqRW5jb2RpbmcgZGVmYXVsdCBjb24gZWwgcXVlIHNlIGd1YXJkYW4gbnVlc3Ryb3MgU2NyaXB0cyoqOiBUb29scyAtPiBHbG9iYWwgT3B0aW9ucyAtPiBDb2RlIC0+IFNhdmluZw0KDQojIERpcmVjdG9yaW9zDQoNClNpZW1wcmUgcXVlIGVzY3JpYmltb3MgZWwgbm9tYnJlIGRlbCBhcmNoaXZvLCBSIGxvIGJ1c2NhIGVuIGVsIF93b3JraW5nIGRpcmVjdG9yeV8uIA0KUGFyYSBzYWJlciBjdWFsIGVzIGVsIGRpcmVjdG9yaW8gZGUgdHJhYmFqbyB1dGlsaXphbW9zIGxhIGZ1bmNpw7NuIGBgYGdldHdkKClgYGAuIA0KUGFyYSByZWRlZmluaXIgZWwgZGlyZWN0b3JpbyBkZSB0cmFiYWpvLCB1dGlsaXphbW9zIGxhIGZ1bmNpw7NuIGBgYHNldHdkYGBgDQoNCl9fTm8gZXMgYWNvbnNlamFibGUgdXRpbGl6YXIgZWwgZGlyZWN0b3JpbyBkZSB0cmFiYWpvLCBzaSBub3Mgb2x2aWRhbW9zIGRlIGRlZmluaXJsbywgdGlyYW1vcyBsb3MgYXJjaGl2b3MgZW4gY3VhbHF1aWVyIGxhZG9fXyANCg0KTG8gbcOhcyBwcsOhY3RpY28gZXMgZGVmaW5pciBsb3MgZGlyZWN0b3Jpb3MgZGUgdHJhYmFqbyBjb21vIHZhbG9yZXMuIHkgcGVnYXIgZWwgbm9tYnJlIGRlbCBhcmNoaXZvIGNvbiBsYXMgY2FycGV0YXMuDQoNCmBgYA0KY2FycGV0YV9mdWVudGVzICAgIDwtIHBhc3RlKCJDOi9Vc2Vycy8uLi4vRG9jdW1lbnRzL1IvZnVlbnRlcy8iKQ0KY2FycGV0YV9yZXN1bHRhZG9zIDwtIHBhc3RlKCJDOi9Vc2Vycy8uLi4vRG9jdW1lbnRzL1IvcmVzdWx0YWRvcy8iKQ0KYGBgDQoNCkVzIGltcG9ydGFudGUgbm90YXIgcXVlIHNlIHV0aWxpemEgbGEgYmFycmEgYGBgL2BgYCBlbiBsdWdhciBkZSBgYGBcYGBgIChxdWUgc2FsZSBkZSBjb3BpYXIgeSBwZWdhciBlbCBkaXJlY3RvcmlvKQ0KDQplbCBub21icmUgY29tcGxldG8gZGVsIGFyY2hpdm8gcHVlZGUgc2VyIA0KYGBgDQphcmNoaXZvX2RhdG9zICAgICAgPC0gcGFzdGUwKGNhcnBldGFfZnVlbnRlcywgImFyY2hpdm9fZnVlbnRlcy50eHQiKQ0KYXJjaGl2b19yZXN1bHRhZG9zIDwtIHBhc3RlMChjYXJwZXRhX3Jlc3VsdGFkb3MsICJhcmNoaXZvX3Jlc3VsdGFkb3MudHh0IikNCmBgYA0KDQpsdWVnbywgcGFyYSBsZWVyIHVuIGV4Y2VsLCBzZSBlc2NyaWJlOiANCg0KYGBgDQp0YWJsYSA8LSByZWFkLnhsc3goZmlsZSA9IGFyY2hpdm9fZGF0b3Msc2hlZXROYW1lID0gImhvamEgMSIpICNjb21vIGVzIHVuYSB2YXJpYWJsZSwgeWEgbm8gbGxldmEgY29taWxsYXMNCg0KYGBgDQoNCiMjIERpcmVjdG9yaW9zIGF1dG9ycmVmZXJlbmNpYWxlcw0KDQpTaSBiaWVuIGV4Y2VkZSBsb3MgYWxjYW5jZXMgZGUgZXN0ZSBjdXJzbywgZGVqYW1vcyB1biBjaHVuayBkZSBjw7NkaWdvIHF1ZSBwdWVkZSByZXN1bHRhciBzdW1hbWVudGUgw7p0aWwgcGFyYSBjcmVhciB1biBkaXJlY3RvcmlvIGRlIHRyYWJham8gcGFyYSB1biBwcm95ZWN0byBudWV2by4NCg0KYGBge3J9DQojaW5zdGFsbC5wYWNrYWdlcyhyc3R1ZGlvYXBpKQ0Kc2NyaXB0LmRpciA8LSBwYXN0ZTAoZGlybmFtZShyc3R1ZGlvYXBpOjpnZXRBY3RpdmVEb2N1bWVudENvbnRleHQoKSRwYXRoKSwiLyIpDQpiYXNlcy5kaXIgIDwtICBwYXN0ZTAoZGlybmFtZShzY3JpcHQuZGlyKSwiL0Z1ZW50ZXMvIikNCiNkaXIuY3JlYXRlKGJhc2VzLmRpcikNCnJlc3VsdGFkb3MuZGlyIDwtIHBhc3RlMChkaXJuYW1lKHNjcmlwdC5kaXIpLCIvUmVzdWx0YWRvcy8iKQ0KI2Rpci5jcmVhdGUocmVzdWx0YWRvcy5kaXIpDQoNCiNjaGVxdWVvDQpkaXIuZXhpc3RzKGJhc2VzLmRpcikNCmRpci5leGlzdHMocmVzdWx0YWRvcy5kaXIpDQoNCmBgYA0KDQpMYSBwcmltZXJhIGzDrW5lYSBfZW5jdWVudHJhXyBsYSBjYXJwZXRhIGRvbmRlIGVzdGEgZ3VhcmRhZG8gZWwgc2NyaXB0IChzaSBubyBlc3RhIGd1YXJkYWRvIG5vIGZ1bmNpb25hKS4gICAgIA0KTGEgc2VndW5kYSBsw61uZWEgY3JlYSBlbCBub21icmUgZGVsIGRpcmVjdG9yaW8gRnVlbnRlcw0KTGEgdGVyY2VyYSBsw61uZWEgKGFudWxhZGEpIGNyZWEgZWwgZGlyZWN0b3JpbyBGdWVudGVzDQpMYSBjdWFydGEgbMOtbmVhIGNyZWEgZWwgbm9tYnJlIGRlbCBkaXJlY3RvcmlvIFJlc3VsdGFkb3MNCkxhIHF1aW50YSBsw61uZWEgKGFudWxhZGEpIGNyZWEgZWwgZGlyZWN0b3JpbyBSZXN1bHRhZG9zDQoNCiMgT3JnYW5pemFjacOzbiBzY3JpcHRzDQoNClBvciDDumx0aW1vLCBlcyBhY29uc2VqYWJsZSBtYW50ZW5lciBlbiB0b2RvcyBsb3Mgc2NyaXB0IHVuYSBtaXNtYSBlc3RydWN0dXJhIGRlbCB0aXBvOg0KDQoxLiBMaW1waWFyIGxhIG1lbW9yaWEgYGBgIHJtKGxpc3Q9bHMoKSkgYGBgICAgIA0KMi4gQ2FyZ2FyIGxpYnJlcsOtYXMNCjMuIERlZmluaXIgZGlyZWN0b3Jpb3MNCjQuIERlZmluaXIgZnVuY2lvbmVzDQo1LiBMZXZhbnRhciBhcmNoaXZvcyAgICAgDQouLi4gcHJvY2VzYW1pZW50byAuLi4uICAgICANCm4uIGdyYWJhciByZXN1bHRhZG9zDQoNClRhbWJpw6luIGVzIMO6dGlsIG9yZ2FuaXphciBsYXMgcGFydGVzIGRlbCBzY3JpcHQgZW4gY2Fww610dWxvcy4gUGFyYSBlc28gICANCg0KDQpgYGAjIyMgZXNjcmliaW1vcyBlbCB0w610dWxvIGRlbCBjYXBpdHVsbyBlbmNlcnJhZG8gZW50cmUgdHJlcyBvIG3DoXMgY29yY2hldGVzICMjI2BgYA0KDQoNCiMgQXl1ZGFzDQoNCkhheSBtdWNoYXMgYXl1ZGFzLCBwcm9waWFzIGRlbCBwcm9ncmFtYSwgbyBkZSB1c3VhcmlvcywgcXVlIHB1ZWRlbiBzZXIgZGUgYXl1ZGEuDQoNCi0gRW4gZWwgcHJvZ3JhbWEsIHBhcmEgY29uc3VsdGFyIGxvcyBwYXLDoW1ldHJvcyBkZSB1bmEgZnVuY2nDs24sIGxlIGVzY3JpYmUgYGBgP2Z1bmNpb24oKWBgYA0KDQotIFtSc3R1ZGlvXShodHRwczovL3d3dy5yc3R1ZGlvLmNvbS9yZXNvdXJjZXMvY2hlYXRzaGVldHMvKSB0aWVuZSB1bm9zIG1hY2hldGVzIG11eSDDunRpbGVzDQoNCi0gW1Jkb2N1bWVudGF0aW9uXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvKQ0KDQotIFtzdGFjayBvdmVyZmxvd10oaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvdGFnZ2VkL3IpIGNvbnZpZW5lIGxsZWdhciBkZXNkZSBnb29nbGUNCg0KTGEgX2NsYXZlXyBlcyBhcHJlbmRlciBsYSB0ZXJtaW5vbG9nw61hIHBhcmEgZ29vZ2xlYXIgZW4gaW5nbGVzIGxhcyBkdWRhcywgeSBwcmVzdGFyIGF0ZW5jacOzbiBhIHF1ZSBsYXMgcmVzcHVlc3RhcyBzZWFuIGFjdHVhbGVzIChSIGVzIHVuIGxlbmd1YWplIF92aXZvXykNCg0KIyBQYXJhIGxhIHJldW5pw7NuIDI6DQoNCmluc3RhbGFyIGxvcyBzaWd1aWVudGVzIHBhcXVldGVzOg0KDQpgYGANCmluc3RhbGwucGFja2FnZXMoYygidGlkeXZlcnNlIiwieGxzeCIsJ3N0YXRhcicsJ3JlbGRpc3QnLCAnZ2d0aGVtZXMnLCAnZ2dyZXBlbCcsICdzY2FsZXMnLCAnZ2dqb3knLCAncnN0dWRpb2FwaScsJ3F1ZXN0aW9ucicsICdhbGx1dmlhbCcpKQ0KYGBgDQoNCiMgRWplcmNpY2lvcyBwYXJhIHByYWN0aWNhcg0KDQotIENyZWFyIHVuIE9CSkVUTyBsbGFtYWRvIF9PQkpFVE9fIGRlZmluaWRvIGNvbW8gZWwgcmVzdWx0YWRvIGRlIGxhIHN1bWE6IDUgKyA2DQotIENyZWFyIHVuIFZFQ1RPUiBfVkVDMF8gcXVlIGNvbnRlbmdhIGxvcyBuw7ptZXJvcyAxLCAzLCA0Lg0KLSBDcmVhciAzIHZlY3RvcmVzICggX1ZFQzFfLCBfVkVDMl8sIF9WRUMzXykgcXVlIHNlYW4gdHJhbnNmb3JtYWNpb25lcyBkZWwgYW50ZXJpb3INCi0gQ3JlYXIgMyB2ZWN0b3JlcyBjb24gbGEgbWlzbWEgY2FudGlkYWQgZGUgZWxlbWVudG9zIHF1ZSBWRUMwLCBwZXJvIGNvbiB2YXJpYWJsZXMgc3RyaW5nICh0ZXh0bykgKCBfVkVDNF8sIF9WRUM1XywgX1ZFQzZfKS4NCi0gQ3JlYXIgdW4gZGF0YWZyYW1lIF9ERlJBTUVfIGNvbW8gY29tYmluYWNpw7NuIGRlIHRvZG9zIGxvcyBfX3ZlY3RvcmVzX18gY3JlYWRvcyBwcmV2aWFtZW50ZQ0KLSBDcmVhciB1bmEgbGlzdGEgY29uIGNhZGEgdW5vIGRlIGxvcyBfX2VsZW1lbnRvc19fIGNyZWFkb3MgcHJldmlhbWVudGUNCg0KLSBQYXJhIHRvZG9zIGxvcyB2YWxvcmVzIGRlbCB2ZWN0b3IgX1ZFQzBfLCBpbXByaW1pciBtZWRpYW50ZSB1biBsb29wIGVsIGRvYmxlIGRlIGRpY2hvcyB2YWxvcmVzDQotIE1lZGlhbnRlIHVuIGxvb3AgcXVlIGl0ZXJlIHNvYnJlIHVuYSBkZSBsYXMgY29sdW1uYXMgIF9fInN0cmluZyJfXyBkZWwgZGF0YWZyYW1lIF9ERlJBTUVfLCBpbXByaW1pciB1bmEgdmFyaWFibGUgcXVlIGNvbWJpbmUgMyBjb2x1bW5hcyBkZSBkaWNobyBkYXRhZnJhbWUNCi0gUmVlc2NyaWJpciBlbCBWRUMxIGRlbCBEQVRBRlJBTUUgcGFyYSBxdWUgc3VzIGVsZW1lbnRvcyBzZWFuOiAgICAgIA0KICAgIC0gIEVsIERvYmxlIGRlIFZFQ18wLCBjdWFuZG8gw6lzdGUgc2VhIG1heW9yIGEgMg0KICAgIC0gIElndWFsZXMgYSBWRUNfMCwgcGFyYSBlbCByZXN0byBkZSBsb3MgY2Fzb3MgDQotIENyZWFyIHVuYSBmdW5jacOzbiBsbGFtYWRhIF9Ib2xhTXVuZG9fIHF1ZSBpbXByaW1hIGVsIHRleHRvICJIb2xhIG11bmRvIg0KLSBDcmVhciB1bmEgZnVuY2nDs24gcXVlIGRldnVlbHZhIGxhIHN1bWF0b3JpYSBkZSBsb3MgbsO6bWVyb3MgZW50ZXJvcyBjb21wcmVuZGlkb3MgZW50cmUgMSB5IHVuIHBhcsOhbWV0cm8gX3hfIGEgZGVmaW5pcg0KDQotIExldmFudGFyIGxhIGJhc2UgSW5kaXZpZHVhbCBkZWwgMWVyIHRyaW1lc3RyZSBkZSAyMDE3LCBkZSBsYSBFUEgNCi0gR3VhcmRhciBsYSBiYXNlIGNvbW8gdW4gYXJjaGl2byBkZSBleHRlbnNpw7NuIC5SRFMNCi0gVm9sdmVyIGEgbGV2YW50YXIgbGEgYmFzZSwgcGVybyBjb21vIC5SRFMgeSBhc2lnbmFybGEgY29uIGVsIG5vbWJyZSBfQmFzZVJEU18gwr90YXJkYSBtw6FzIG8gbWVub3M/DQotIExldmFudGFyIGRlbCBFeGNlbCBsbGFtYWRvIENBTkFTVEFTIHF1ZSBzZSBlbmN1ZW50cmEgZW4gbGEgY2FycGV0YSBkZSBGdWVudGVzLCBsYSBob2phICJDQlQiIHkgZGVmaW5pcmxhIGNvbW8gX0hvamFDQlRfLiAgUHVlZGVuIHVzYXIgbGEgZnVuY2nDs246DQogICAgLSByZWFkLnhsc3ggIGRlIGxhIGxpYnJlcsOtYSBfX3hsc3hfXyANCiAgICAtIHJlYWRfZXhjZWwgZGUgbGEgbGlicmVyw61hIF9fcmVhZHhsX18NCg0KLSBMZXZhbnRhciBlbCBtaXNtbyBFeGNlbCwgdXRpbGl6YW5kbyB1biBPYmpldG8gcXVlIGNvbnRlbmdhIGVsIGRpcmVjdG9yaW8gZGVsIGFyY2hpdm8gYSBsZXZhbnRhci4NCiAgICAtIENoZXF1ZWFyIGNvbiBgYGBkaXIuZXhpc3QoKWBgYCBxdWUgbG8gY3JlYW1vcyBiaWVuICjCv25vIGZ1bmNpb27Dsz8gcGlzdGE6IC9cXCkNCi0gRWplY3V0YXIgbG9zIHNpZ3VpZW50ZXMgY29tYW5kb3M6DQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KaW5zdGFsbC5wYWNrYWdlcyhyc3R1ZGlvYXBpKSAjIFNPTE8gTEEgUFJJTUVSQSBWRVoNCg0Kc2NyaXB0LmRpciA8LSBwYXN0ZTAoZGlybmFtZShyc3R1ZGlvYXBpOjpnZXRBY3RpdmVEb2N1bWVudENvbnRleHQoKSRwYXRoKSwiLyIpDQoNCnNjcmlwdC5kaXINCg0KYmFzZXMuZGlyICA8LSAgcGFzdGUwKGRpcm5hbWUoc2NyaXB0LmRpciksIi9GdWVudGVzLyIpDQoNCmJhc2VzLmRpcg0KDQpkaXIuY3JlYXRlKGJhc2VzLmRpcikNCg0KcmVzdWx0YWRvcy5kaXIgPC0gcGFzdGUwKGRpcm5hbWUoc2NyaXB0LmRpciksIi9SZXN1bHRhZG9zLyIpDQoNCnJlc3VsdGFkb3MuZGlyDQoNCmRpci5jcmVhdGUocmVzdWx0YWRvcy5kaXIpDQoNCmRpci5leGlzdHMoYmFzZXMuZGlyKQ0KYGBgDQoNCsK/cXXDqSBzdWNlZGnDsz8gRXhwbGljYXIgcXVlIHJlYWxpemEgY2FkYSB1bmEgZGUgbGFzIGzDrW5lYXMuDQoNCg0K